import "webrtc-adapter";
import _ from "lodash";

class MyWebRTC {
  ws = null;
  me = null;
  peerConns = {}; //所有对等端{[name]:pc}
  dataCallBack;
  streamCallBack;

  constructor(wsUrl, onData, onStream) {
    this.dataCallBack = onData;
    this.streamCallBack = onStream;
    this.ws = this.createWS(wsUrl, (message) => {
      const data = JSON.parse(message);
      switch (data.type) {
        case "onLogin": // 某用户登录
          this.handleLogin(data.name);
          break;
        case "onOffer": // 他人邀请我
          this.handleOffer(data.offer, data.name);
          break;
        case "onAnswer": // 他人应答我
          this.handleAnswer(data.answer, data.name);
          break;
        case "onCandidate": // 他人候选信息给我
          this.handleCandidate(data.candidate, data.name);
          break;
        case "onLeave": // 他人离线
          this.handleLeave(data.name);
          break;
        default:
          break;
      }
    });
  }

  // 抽象方法，具体由继承类实现.创建数据通道
  requestChannel(pc, name) {}
  responseChannel(pc, name) {}

  createWS = (url, cb) => {
    const ws = new WebSocket(url);
    ws.onopen = function () {
      console.log(`${url} connected`);
    };
    ws.onmessage = function (e) {
      cb(e.data);
    };
    // 需要服务端响应了才会触发，因此服务器端延时会造成客户端关闭延时
    ws.onclose = function () {
      console.log(`${url} closed`);
    };
    ws.onerror = function (e) {
      console.log(`${url} ${e.msg || "connect failed"}`);
    };
    return ws;
  };

  // 处理某用户登录
  handleLogin = (name) => {
    console.log(name, "登录系统");
    this.me = name;
  };

  // 处理某用户呼叫
  handleOffer = async (offer, name) => {
    if (name && offer) {
      console.log(name, "邀请", this.me);
      // 应答他人需要新的点对点连接
      const pc = this.createPC(name);
      await pc.setRemoteDescription(new RTCSessionDescription(offer));
      // 此时开启本地流媒体，注意顺序
      await this.responseChannel(pc, name);
      const answer = await pc.createAnswer();
      await pc.setLocalDescription(answer);

      this.ws.send(
        JSON.stringify({
          type: "answer",
          answer: answer,
          name,
        })
      );
    }
  };

  // 处理某用户应答
  handleAnswer = async (answer, name) => {
    console.log(name, "应答", this.me);
    const pc = this.peerConns[name];
    if (pc && answer) {
      // 保存远程应答会话
      await pc.setRemoteDescription(new RTCSessionDescription(answer));
    }
  };

  // 处理某用户发送候选信息
  handleCandidate = async (candidate, name) => {
    console.log(name, "发送候选信息给", this.me, candidate);
    const pc = this.peerConns[name];
    if (pc) {
      await pc.addIceCandidate(new RTCIceCandidate(candidate));
    }
  };

  // 处理我对某人的连接断开
  handleLeave = (name) => {
    const pc = this.peerConns[name];
    if (pc) {
      // 关闭本地数据通道
      if (pc.channel) {
        pc.channel.close();
      }

      // 关闭本人对于他人的连接
      pc.close();
      delete this.peerConns[name];
    }
  };

  // 创建与他人点对点连接
  createPC = (name) => {
    // creating our RTCPeerConnection object
    if (!RTCPeerConnection) {
      alert("浏览器不支持webrtc");
      return;
    }
    const iceConfig = {
      iceServers: [{ urls: "stun:stun2.1.google.com:19302" }],
    };
    const pc = new RTCPeerConnection(iceConfig);
    //ICE候选发送到远程对等方,只要本地代理ICE需要通过信令服务器传递信息给其他对等端时就会触发
    //createOffer或createAnswer生成的SDP调用setLocalDescription，ICE采集就会开始
    pc.onicecandidate = (event) => {
      console.log(this.me, "检测到候选信息，即将发给", name);
      if (event.candidate) {
        this.ws.send(
          JSON.stringify({
            type: "candidate",
            candidate: event.candidate,
            name,
          })
        );
      }
    };
    // 监听对等端流媒体
    pc.ontrack = (e) => {
      if (e && e.streams) {
        // 对方流回调
        this.streamCallBack(name, e.streams[0]);
      }
    };
    // 该连接针对的对象名
    pc.myname = name;
    this.peerConns[name] = pc;
    return pc;
  };

  //个人登录
  join = (name) => {
    if (name) {
      // 发送登录信息给服务器
      this.ws.send(JSON.stringify({ type: "login", name }));
    }
  };

  // 呼叫他人
  offer = async (name) => {
    const pc = this.createPC(name);
    await this.requestChannel(pc, name);
    // 数据通道打开以后创建请求
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    this.ws.send(
      JSON.stringify({
        type: "offer",
        offer: offer,
        name,
      })
    );
  };

  // 本人下线并通知他人
  leave = (name) => {
    // 通知他人关闭
    this.ws.send(
      JSON.stringify({
        type: "leave",
        name,
      })
    );

    // 本地流存在则停止
    if (this.localStream) {
      this.localStream.getTracks().forEach((track) => {
        track.stop();
      });
    }

    this.handleLeave(name);
  };

  // 释放
  dispose = () => {
    this.me = null;
    // 遍历所有的点对点连接并关闭
    _.forEach(this.peerConns, (pc) => {
      this.leave(pc.myname);
    });
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
    this.peerConns = null;
  };
}
export default MyWebRTC;
